﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using EPiServer;
using EPiServer.Cms.Shell.Extensions;
using EPiServer.Cms.Shell.UI.ObjectEditing;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.Framework.Serialization;
using EPiServer.Shell.ObjectEditing;
using EPiServer.Shell.ObjectEditing.EditorDescriptors;
using EPiServer.Shell.UI.Models;
using EPiServer.Shell.UI.Rest;

namespace OpenWaves.EPiServer.GenericListProperty
{
    /// <summary>
    /// Register an editor for StringList properties
    /// </summary>
   // [EditorDescriptorRegistration(TargetType = typeof(IEnume), UIHint = PropertyListEditorDescriptor.UIHint)]
    public class PropertyListEditorDescriptor : EditorDescriptor
    {
        public const string UIHint = "ow.list";


        private readonly ExtensibleMetadataProvider _metadataProvider;
        private readonly IMetadataStoreModelCreator _modelCreator;
        private readonly IObjectSerializerFactory _serializerFactory;

        public PropertyListEditorDescriptor()
        {
            var locator = global::EPiServer.ServiceLocation.ServiceLocator.Current;
            _metadataProvider = locator.GetInstance<ExtensibleMetadataProvider>();
            _modelCreator = locator.GetInstance<IMetadataStoreModelCreator>();
            _serializerFactory = locator.GetInstance<IObjectSerializerFactory>();
        }


        const string DefaultistWidgetClassName = "ow-epi-listProperty.widgets.ListPropertyWidget";

        public override void ModifyMetadata(ExtendedMetadata metadata,
                                            IEnumerable<Attribute> attributes)
        {
            base.ModifyMetadata(metadata, attributes);
            ExtendedMetadata mostContentMetadata = ExtendedMetadataExtensions.FindTopMostContentMetadata(metadata);
            IContent content = mostContentMetadata.Model as IContent;
            if (content == null)
                return;
            metadata.EditorConfiguration.Add("contentTypeId", (object)content.ContentTypeID);
            if (content.ContentLink != (ContentReference)null)
                metadata.EditorConfiguration.Add("contentLink", (object)content.ContentLink);
            IResourceable resourceable = content as IResourceable;
            if (resourceable == null)
                return;
            metadata.EditorConfiguration.Add("parentLink", (object)content.ParentLink);
  //          metadata.EditorConfiguration.Add("resourceFolderId", (object)resourceable.ContentFolderID);
  //          mostContentMetadata.AdditionalValues["ResourceFolderId"] = (object)resourceable.ContentFolderID;

            // list item metadata
            string itemDisplayNamePropertyName;
            metadata.CustomEditorSettings["ListModelProperties"] = CreateMetadataViewModel(metadata, content, out itemDisplayNamePropertyName);
            metadata.EditorConfiguration.Add("ItemDisplayNamePropertyName", itemDisplayNamePropertyName);

            var attributesList = attributes.ToList();

            // custom editor
            var listItemWidget = attributesList.OfType<IListItemWidget>().SingleOrDefault();
            if (listItemWidget != null)
            {
                if (metadata.Model == null)
                {
                    listItemWidget.PropertyValue = null;
                }
                else
                {
                    var propertyValue = ((PropertyData)(metadata.Model)).Value;
                    listItemWidget.PropertyValue = propertyValue;
                }

                if (string.IsNullOrWhiteSpace(listItemWidget.WidgetClass))
                {
                    this.ClientEditingClass = DefaultistWidgetClassName;
                }
                else
                {
                    this.ClientEditingClass = listItemWidget.WidgetClass;
                }

                metadata.EditorConfiguration.Add("ItemWidgetSettings", listItemWidget.WidgetSettings);
            }
            else
            {
                this.ClientEditingClass = DefaultistWidgetClassName;
            }

            // custom setings
            var listSettings = attributesList.OfType<GenericListPropertySettingsAttribute>().SingleOrDefault();
            if (listSettings == null)
            {
                metadata.EditorConfiguration.Add("ListSettings", new GenericListPropertySettingsAttribute());
            }
            else
            {
                metadata.EditorConfiguration.Add("ListSettings", listSettings);
            }
        }

        private  MetadataStoreModel CreateMetadataViewModel(ExtendedMetadata metadata, IContent content, out string itemDisplayNamePropertyName)
        {
            /*
            ContentReference contentReference;
            if (ContentReference.IsNullOrEmpty(content.ContentLink))
            {
                if (content is BlockData)
                {
                    contentReference = content.ParentLink;
                }
                else
                {
                    content = DataFactory.Instance.GetPage((PageReference)content.ParentLink);
                    contentReference = content.ContentLink;
                }
            }
            else
            {
                contentReference = content.ContentLink;
            }

            var contentBlockType = metadata.ModelType.GetGenericArguments()[0];

            var serviceLocator = global::EPiServer.ServiceLocation.ServiceLocator.Current;
            var contentRepository = serviceLocator.GetInstance<IContentRepository>();

            var contentData = (IContentData)contentRepository
                                                 .GetType()
                                                 .GetMethod("GetDefault", new[] { typeof(ContentReference) })
                                                 .MakeGenericMethod(contentBlockType)
                                                 .Invoke(contentRepository, new object[] { contentReference });

            var extensibleMetadataProvider = new ExtensibleMetadataProvider();
            var modelMetadata = extensibleMetadataProvider.GetMetadataForType(() => contentData, typeof(ContentData));

            var localizationService = serviceLocator.GetInstance<LocalizationService>();
            var metadataViewModel = MetadataViewModel.CreateFromModelMetadata(modelMetadata as ExtendedMetadata, localizationService);

            var i = 0;
            while (i < metadataViewModel.Properties.Count)
            {
                var viewModelProperty = metadataViewModel.Properties[i];
                if (viewModelProperty.IsStandardProperty())
                {
                    metadataViewModel.Properties.Remove(viewModelProperty);
                }
                else
                {
                    i++;
                }
            }

            // get DisplayName item property name
            var displayNameProperty = modelMetadata.Properties.OfType<ContentDataMetadata>().FirstOrDefault(p => p.Attributes.OfType<ItemDisplayNameAttribute>().Any());
            if (displayNameProperty == null)
            {
                itemDisplayNamePropertyName = string.Empty;
            }
            else
            {
                itemDisplayNamePropertyName = displayNameProperty.PropertyName.ToJavascriptPropertyName();
            }

            */















            /*
            ContentReference contentReference;
            if (ContentReference.IsNullOrEmpty(content.ContentLink))
            {
                if (content is BlockData)
                {
                    contentReference = content.ParentLink;
                }
                else
                {
                    content = DataFactory.Instance.GetPage((PageReference)content.ParentLink);
                    contentReference = content.ContentLink;
                }
            }
            else
            {
                contentReference = content.ContentLink;
            }



            var contentBlockType = metadata.ModelType.GetGenericArguments()[0];
            var c = global::EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<IContentTypeRepository>();
            var contentType = c.Load(contentBlockType.Name);



            var serviceLocator = global::EPiServer.ServiceLocation.ServiceLocator.Current;


            var contentRepository = serviceLocator.GetInstance<IContentRepository>();
            var contentData = (IContentData)contentRepository
                                                .GetType()
                                                .GetMethod("GetDefault", new[] { typeof(ContentReference) })
                                                .MakeGenericMethod(contentBlockType)
                                                .Invoke(contentRepository, new object[] { contentReference });


            var pageData = contentRepository.Get<IContent>(new ContentReference(6735));
            var _providerManager = serviceLocator.GetInstance<IContentProviderManager>();


            var parentContent = new ContentReference("6735");

            IContent defaultContent = _providerManager.ProviderMap.GetProvider(parentContent).GetDefaultContent(pageData, contentType.ID, LanguageSelector.AutoDetect(true));


            var extensibleMetadataProvider = new ExtensibleMetadataProvider();
            ModelMetadata modelMetadata = extensibleMetadataProvider.GetMetadataForType(() => contentData, typeof(ContentData));






            itemDisplayNamePropertyName = string.Empty;

            var id = contentType.ModelType.ToString();
            var modelAccessor = "{'parentLink':'17742','contentTypeId':'138'}";





            Type type = TypeResolver.GetType(id);


            bool flag = modelAccessor != null;
            Func<object> modelAccessor1;
            var type1 = typeof (ContentData);
                modelAccessor1 = flag ? this.GetModelAccessor(modelAccessor, type1) : (Func<object>)null;
          
                ModelMetadata metadataForType = this._metadataProvider.GetMetadataForType(modelAccessor1, type);
                if (metadataForType != null)
                {
                    MetadataStoreModel metadataStoreModel = this._modelCreator.Create(metadataForType);
                    
                    
                    //if (!flag)
                        return metadataStoreModel;
                   




                }

            return null;
             */



            var serviceLocator = global::EPiServer.ServiceLocation.ServiceLocator.Current;


            var contentBlockType = metadata.ModelType.GetGenericArguments()[0];
            var c = global::EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<IContentTypeRepository>();
            var contentType = c.Load(contentBlockType.Name);



            ContentReference contentReference;
            if (ContentReference.IsNullOrEmpty(content.ContentLink))
            {
                content = DataFactory.Instance.GetPage((PageReference) content.ParentLink);
                contentReference = content.ContentLink;
            }
            else
            {
                contentReference = content.ContentLink;
            }

            var contentRepository = serviceLocator.GetInstance<IContentRepository>();
            var pageData = contentRepository.Get<IContent>(contentReference);

            
            var _providerManager = serviceLocator.GetInstance<IContentProviderManager>();
            IContent defaultContent = _providerManager.ProviderMap.GetProvider(contentReference)
                                                      .GetDefaultContent(pageData, contentType.ID,
                                                                         LanguageSelector.AutoDetect(true));


            var extensibleMetadataProvider = serviceLocator.GetInstance<ExtensibleMetadataProvider>();
            ModelMetadata modelMetadata = extensibleMetadataProvider.GetMetadataForType(() => defaultContent, typeof(ContentData));


            MetadataStoreModel metadataStoreModel = this._modelCreator.Create(modelMetadata);







            // get DisplayName item property name
            var displayNameProperty = modelMetadata.Properties.OfType<ContentDataMetadata>().FirstOrDefault(p => p.Attributes.OfType<ItemDisplayNameAttribute>().Any());
            if (displayNameProperty == null)
            {
                itemDisplayNamePropertyName = string.Empty;
            }
            else
            {
                itemDisplayNamePropertyName = displayNameProperty.PropertyName.ToJavascriptPropertyName();
            }

            var i = 0;
            while (i < metadataStoreModel.Properties.Count)
            {
                var viewModelProperty = metadataStoreModel.Properties[i];
                if (viewModelProperty.IsStandardProperty())
                {
                    metadataStoreModel.Properties.Remove(viewModelProperty);
                }
                else
                {
                    i++;
                }
            }


            return metadataStoreModel;


        }



        private Func<object> GetModelAccessor(string modelAccessorParam, Type t)
        {
            Dictionary<string, string> arguments = this._serializerFactory.GetSerializer("application/json").Deserialize<Dictionary<string, string>>((TextReader)new StringReader(modelAccessorParam));
            return this._metadataProvider.MetadataHandlerRegistry.GetModelAccessor(t, arguments);
        }
    }
}
